﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FortuneCookie.EPiServer.SearchAndReplace;
using FortuneCookie.EPiServer.SearchAndReplace.Data;
using EPiServer.DataAccess;
using EPiServer.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using SearchAndReplaceUnitTests.Environment;
using DynamicProperty = EPiServer.DataAbstraction.DynamicProperty;

namespace SearchAndReplaceUnitTests
{
    /// <summary>
    /// Tests
    /// </summary>
    [TestClass]
    public class Tests
    {

        #region Members and constants

        private const string PAGE_TYPE_NAME = "SearchAndReplacePageType";
        private const string MAIN_CONTENT = "This is the main content.<br /> and here is some more content and some text to replace";
        private static int _sarRootId;
        private static List<SearchAndReplace> _searchAndReplaces = new List<SearchAndReplace>();

        #endregion Members and constants

        #region Properties

        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        private string SummaryContent
        {
            get
            {
                return "".PadLeft(245, 'a') + " toreplace";
            }
        }

        #endregion Properties

        #region EPiServer Data setup methods

        [AssemblyInitialize]
        public static void Initialize(TestContext context)
        {

            EPiServerInitializer.Initialize(context);

        }

        [TestInitialize]
        public void InitializeEPiServer()
        {
            if (!TestContext.TestName.StartsWith("EPiServer_"))
                return;

            CreateTestData();
        }

        [TestCleanup]
        public void Cleanup()
        {
            if (!TestContext.TestName.StartsWith("EPiServer_"))
                return;

            // delete pages
            try
            {
                PageData page = DataFactory.Instance.GetPage(new PageReference(_sarRootId, true));
                DataFactory.Instance.DeleteChildren(page.PageLink, true, AccessLevel.NoAccess);
                DataFactory.Instance.Delete(page.PageLink, true, AccessLevel.NoAccess);
            }
            catch
            {
            }

            // delete page type
            try
            {
                PageType pageTypeData = PageType.Load(PAGE_TYPE_NAME);
                pageTypeData.Delete();
            }
            catch
            {
            }

            // delete dynamic properties
            PageDefinition pageDefinition = PageDefinition.ListDynamic().Where(current => current.Name == "DynProp").FirstOrDefault();
            if (pageDefinition != null)
                pageDefinition.Delete();

            // delete search and replaces
            IDataHelper dataHelper = CreateDataHelper();

            foreach (SearchAndReplace searchAndReplace in _searchAndReplaces)
                dataHelper.Delete(searchAndReplace);

            _searchAndReplaces.Clear();
        }

        private void CreateTestData()
        {
            Cleanup();

            // Create new page type
            PageType pageTypeData = new PageType
                                        {
                                            Name = PAGE_TYPE_NAME,
                                            Description = "Search and replace testing page type",
                                            FileName = "~/Test.aspx",
                                            IsAvailable = true,
                                            SortOrder = 100,
                                            Defaults = null,
                                            AllowedPageTypes = new int[] {},
                                            
                                        };

            pageTypeData.Save();
            pageTypeData = PageType.Load(PAGE_TYPE_NAME);
            pageTypeData.AllowedPageTypes = new[] { pageTypeData.ID };
            pageTypeData.Save();

            // add a LongString property to the page type
            PageDefinition pageDefinition = new PageDefinition
                                                {
                                                    PageTypeID = pageTypeData.ID,
                                                    Name = "MainContent",
                                                    Required = false,
                                                    EditCaption = "Main content",
                                                    Tab = TabDefinition.List()[0],
                                                    LanguageSpecific = true,
                                                    Type = PageDefinitionType.List().Find(current => string.Equals(current.Name, "LongString"))
                                                };
            pageDefinition.Save();

            // add a short string property to the page type
            pageDefinition = new PageDefinition
            {
                PageTypeID = pageTypeData.ID,
                Name = "Summary",
                Required = false,
                EditCaption = "Summary",
                Tab = TabDefinition.List()[0],
                LanguageSpecific = true,
                Type = PageDefinitionType.List().Find(current => string.Equals(current.Name, "String"))
            };
            pageDefinition.Save();

            // create a new dynamic property
            pageDefinition = new PageDefinition
            {
                Name = "DynProp",
                Required = false,
                EditCaption = "Dynamic Property",
                Tab = TabDefinition.List()[0],
                LanguageSpecific = true,
                Type = PageDefinitionType.List().Find(current => string.Equals(current.Name, "LongString")),
                DefaultValueType = DefaultValueType.Inherit,
                DefaultValue = "This is the default dynamic property value"
            };

            pageDefinition.Save();

            Languages languages = GetLanguages();

            // create a new root Published page
            PageData root = CreatePage(PageReference.StartPage, "sarRoot", languages.MasterLanguageSelector, SaveAction.Publish, true);
            SetDynamicPropertyValue(root);
            _sarRootId = root.PageLink.ID;

            // Create a child page which is just saved
            CreatePage(root.PageLink, "ChildOne", languages.MasterLanguageSelector, SaveAction.Save);

            // Create a child page which is published
            PageData page = CreatePage(root.PageLink, "ChildTwo", languages.MasterLanguageSelector, SaveAction.Publish);
            SetDynamicPropertyValue(page);

            // Create foreign pages

            // create a new root Published page
            root = DataFactory.Instance.CreateLanguageBranch(root.PageLink, languages.SecondaryLanguageSelector);
            root.PageName = "sarRoot";
            root["MainContent"] = MAIN_CONTENT;
            DataFactory.Instance.Save(root, SaveAction.Publish, AccessLevel.NoAccess);

            // Create a child page which is just saved
            page = CreatePage(root.PageLink, "ChildThree", languages.SecondaryLanguageSelector, SaveAction.Save);
            SetDynamicPropertyValue(page);
        }

        private PageData CreatePage(PageReference parent, string pageName, ILanguageSelector languageSelector, SaveAction saveAction)
        {
            return CreatePage(parent, pageName, languageSelector, saveAction, false);
        }

        private PageData CreatePage(PageReference parent, string pageName, ILanguageSelector languageSelector, SaveAction saveAction, bool root)
        {
            PageData page = DataFactory.Instance.GetDefaultPageData(parent, PageType.Load(PAGE_TYPE_NAME).ID, languageSelector);
            page.ParentLink = parent;
            page.PageName = pageName;
            page["MainContent"] = MAIN_CONTENT;
            page["Summary"] = (root ? SummaryContent : string.Empty);
            DataFactory.Instance.Save(page, saveAction, AccessLevel.NoAccess);
            return page;
        }

        private void SetDynamicPropertyValue(PageData page)
        {
            // set dynamic property value
            DynamicPropertyCollection dynamicProperties = DynamicProperty.ListForPage(page.PageLink);

            foreach (DynamicProperty dynamicProperty in dynamicProperties)
            {
                if (dynamicProperty.PropertyValue.Name != "DynProp" || dynamicProperty.LanguageBranch != page.LanguageBranch)
                    continue;

                dynamicProperty.PropertyValue.Value = MAIN_CONTENT;
            }

            DynamicProperty.SaveCollection(page.PageLink, dynamicProperties);
        }

        #endregion EPiServer Data setup methods

        #region Test methods

        [TestMethod]
        public void FindAndReplace_IgnoreCase()
        {
            MethodInfo replaceMethod = GetFindAndReplaceMethod();
            
            // Test 1
            SearchAndReplace searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            string originalValue = "this is the string";
            string newValue = "that is the string";
            string actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 2
            originalValue = "THIS is the string";
            newValue = "that is the string";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 3
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello this is a test <a href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello that is a test <a href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 4
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello THIS is a test <a href=\"test.aspx\" title=\"THIS is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello that is a test <a href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 5
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "<this>hello this is a test <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello that is a test <a this=\"that\" href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 6
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "<this>hello this is a test <selfClosingTag /> <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello that is a test <selfClosingTag /> <a this=\"that\" href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 7
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "<this>hello this is a test <img src=\"#\" alt=\"asdf\" /> <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello that is a test <img src=\"#\" alt=\"asdf\" /> <a this=\"that\" href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 8
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "<a href=\"#this\">this</a>" };
            replaceMethod = GetFindAndReplaceMethod();
            originalValue = "hello this is a test";
            newValue = "hello <a href=\"#this\">this</a> is a test";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 9 (we can replace markup if the text to find is html!
            searchAndReplace = new SearchAndReplace { Find = "<a href=\"#this\">this</a>", Replace = "this" };
            replaceMethod = GetFindAndReplaceMethod();
            originalValue = "hello <a href=\"#this\">this</a> is a test";
            newValue = "hello this is a test";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 10 (we can't replace markup if the text to find is not html!
            searchAndReplace = new SearchAndReplace { Find = "<a href=\"#this\">this</a>", Replace = "this" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello <a href=\"#this\">this</a> is a test";
            newValue = "hello this is a test";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreNotEqual(newValue, actualValue, false);

            // Test 11
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "<a href=\"#this\">this</a>" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello this is a test";
            newValue = "hello <a href=\"#this\">this</a> is a test";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 12
            searchAndReplace = new SearchAndReplace { Find = "this", Replace = "<a href=\"#this\">this</a>" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello&nbsp;this is a test";
            newValue = "hello&nbsp;<a href=\"#this\">this</a> is a test";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 13
            searchAndReplace = new SearchAndReplace { Find = "business", Replace = "<a href=\"#business\">BUSINESS</a>" };
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue= @"<P>As a partner, you can: </P>
<UL>
<LI>use&nbsp; the Development partner logo: recognition of your organisation as an employer of choice and your commitment to staff development </LI>
<LI>have a single signoff process: employees only have to maintain one development record </LI>
<LI>demonstrate credibility and management accounting excellence to other clients and bodies </LI>
<LI>bespoke development plans: these can reflect individual and business needs wider than . </LI>
<LI>a proven approach to CPD: supports the organisation's quality assurance systems and demonstrates commitment and compliance. </LI>
<LI>indicates to employees that their lifelong development is being effectively managed<STRONG> </STRONG>and that their records meet requirements</LI></UL>
<P>Positive business benefits include:</P>
<UL>
<LI>assurance that the business and scheme members are getting maximum benefit from CPD </LI>
<LI>lower staff turnover </LI>
<LI>improved business decisions with employees being up to date in requirements for their jobs </LI>
<LI>better management of business risk through enhanced staff ethical standards </LI>
<LI>no individual or business time wasted through additional paperwork and bureaucracy </LI>
<LI>enables employers to attract and retain high quality staff through demonstrably implementing best practice people development. </LI>
<LI>enhanced professional standing of the organisation in the marketplace. </LI>
<LI>broad organisational flexibility with management accounting professionals who can adapt to changing business needs - cost effective use of human resources </LI>
<LI>potential for consistency across all professionals in the organisation through this planned business and individual centric approach to CPD</LI></UL>";
            newValue = originalValue.Replace(searchAndReplace.Find, searchAndReplace.Replace);
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);
        }

        [TestMethod]
        public void FindAndReplace_RespectCase()
        {
            MethodInfo replaceMethod = GetFindAndReplaceMethod();

            // Test 1
            SearchAndReplace searchAndReplace = new SearchAndReplace { Find = "this", Replace = "that", MatchCase = true};
            string originalValue = "this is the string";
            string newValue = "that is the string";
            string actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 2
            originalValue = "THIS is the string";
            newValue = "that is the string";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreNotEqual(newValue, actualValue, false);

            // Test 3
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello this is a test <a href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello that is a test <a href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 4
            originalValue = "hello THIS is a test <a href=\"test.aspx\" title=\"THIS is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello that is a test <a href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreNotEqual(newValue, actualValue, false);

            // Test 5
            originalValue = "<this>hello this is a test <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello that is a test <a this=\"that\" href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 6
            originalValue = "<this>hello THIS is a test <a this=\"that\" href=\"test.aspx\" title=\"THIS is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello that is a test <a this=\"that\" href=\"test.aspx\" title=\"that is a test\">that is the test of all tests</a> and that is more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreNotEqual(newValue, actualValue, false);
        }

        [TestMethod]
        public void FindAndReplace_SearchWithinWords()
        {
            MethodInfo replaceMethod = GetFindAndReplaceMethod();

            // Test 1
            SearchAndReplace searchAndReplace = new SearchAndReplace { Find = "is", Replace = "was", SearchWithinWords = false};
            string originalValue = "hello this is the string";
            string newValue = "hello this was the string";
            string actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 2
            searchAndReplace.SearchWithinWords = true;
            originalValue = "hello this is the string";
            newValue = "hello thwas was the string";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 4
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "is a";
            originalValue = "hell0 again, this a is a test!";
            newValue = "hell0 again, this a was test!";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 5
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "a test!";
            originalValue = "hell0 again, this is a test!";
            newValue = "hell0 again, this is was";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 6
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "(is) a";
            originalValue = "hell0 again, this (is) a test!";
            newValue = "hell0 again, this was test!";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);


            // Test 7
            searchAndReplace = new SearchAndReplace { Find = "is", Replace = "was", SearchWithinWords = true };
            searchAndReplace.SearchWithinWords = false;
            replaceMethod = GetFindAndReplaceInHtmlMethod();
            originalValue = "hello this is a test <a href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello this was a test <a href=\"test.aspx\" title=\"this was a test\">this was the test of all tests</a> and this was more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 8
            searchAndReplace.SearchWithinWords = true;
            originalValue = "hello this is a test <a href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more";
            newValue = "hello thwas was a test <a href=\"test.aspx\" title=\"thwas was a test\">thwas was the test of all tests</a> and thwas was more";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 9
            searchAndReplace.SearchWithinWords = false;
            originalValue = "<this>hello this is a test <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello this was a test <a this=\"that\" href=\"test.aspx\" title=\"this was a test\">this was the test of all tests</a> and this was more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 10
            searchAndReplace.SearchWithinWords = true;
            originalValue = "<this>hello this is a test <a this=\"that\" href=\"test.aspx\" title=\"this is a test\">this is the test of all tests</a> and this is more</this>";
            newValue = "<this>hello thwas was a test <a this=\"that\" href=\"test.aspx\" title=\"thwas was a test\">thwas was the test of all tests</a> and thwas was more</this>";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 11
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "http://www.google.co.uk";
            searchAndReplace.Replace = "http://www.froogle.co.uk";
            originalValue = "some text <a href=\"http://www.google.co.uk\">link</a> some more text";
            newValue = "some text <a href=\"http://www.froogle.co.uk\">link</a> some more text";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 12
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "and";
            searchAndReplace.Replace = "hand";
            originalValue = "this and that";
            newValue = "this hand that";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 13
            searchAndReplace.SearchWithinWords = false;
            searchAndReplace.Find = "and";
            searchAndReplace.Replace = "hand";
            originalValue = "this hand that";
            newValue = "this hand that";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);

            // Test 14
            searchAndReplace.SearchWithinWords = true;
            searchAndReplace.Find = "and";
            searchAndReplace.Replace = "hand";
            originalValue = "this hand that";
            newValue = "this hhand that";
            actualValue = (string)replaceMethod.Invoke(null, new object[] { originalValue, searchAndReplace, false });
            Assert.AreEqual(newValue, actualValue, false);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingMasterLanguageBranch()
        {
            Languages languages = GetLanguages();
            TestFindAndReplaceInLanguageBranch(languages.MasterLanguageBranch, languages.MasterLanguageSelector, 3, false, 0);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingSecondaryLanguageBranch()
        {
            Languages languages = GetLanguages();
            TestFindAndReplaceInLanguageBranch(languages.SecondaryLanguageBranch, languages.SecondaryLanguageSelector, 2, false, 0);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingAllLanguageBranchesAndDynamicProperties()
        {
            TestFindAndReplaceInLanguageBranch(string.Empty, null, 5, false, 0);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingMasterLanguageBranchAndDynamicProperties()
        {
            Languages languages = GetLanguages();
            TestFindAndReplaceInLanguageBranch(languages.MasterLanguageBranch, languages.MasterLanguageSelector, 3, true, 2);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingSecondaryLanguageBranchAndDynamicProperties()
        {
            Languages languages = GetLanguages();
            TestFindAndReplaceInLanguageBranch(languages.SecondaryLanguageBranch, languages.SecondaryLanguageSelector, 2, true, 1);
        }

        [TestMethod]
        public void EPiServer_TestUpdatingAllLanguageBranches()
        {
            TestFindAndReplaceInLanguageBranch(string.Empty, null, 5, true, 3);
        }

        [TestMethod]
        public void EPiServer_ReplacePlainTextWithMarkup()
        {
            SearchAndReplace searchAndReplace = new SearchAndReplace
                                                    {
                                                        Created = DateTime.Now,
                                                        CreatedBy = "Tester",
                                                        StartPage = _sarRootId.ToString(),
                                                        StartPageOnly = true,
                                                        SearchWithinWords = true,
                                                        Find = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa toreplace",
                                                        Replace = "<a href=\"#link\" title=\"a link\">a link</a>"
                                                    };

            MethodInfo method = GetFindAndReplaceThreadedMethod();

            method.Invoke(null, new object[] {searchAndReplace});

            // assert values in page object
            Guid id = searchAndReplace.Id;
            _searchAndReplaces.Add(searchAndReplace);
            IDataHelper dataHelper = CreateDataHelper();
            searchAndReplace = dataHelper.GetById(id);
            PageData page = DataFactory.Instance.GetPage(new PageReference(searchAndReplace.Pages[0].PageId, searchAndReplace.Pages[0].WorkId));
            string propertyValue = page.Property["Summary"].Value.ToString();
            Assert.AreEqual(SummaryContent.Replace(searchAndReplace.Find, searchAndReplace.Replace), propertyValue);

            // then update some html in the main content
            searchAndReplace = new SearchAndReplace
            {
                Created = DateTime.Now,
                CreatedBy = "Tester",
                StartPage = _sarRootId.ToString(),
                StartPageOnly = true,
                Find = "main content",
                Replace = "<a href=\"#link\" title=\"a link\">a link</a>"
            };

            method.Invoke(null, new object[] { searchAndReplace });

            // assert values in page object
            id = searchAndReplace.Id;
            _searchAndReplaces.Add(searchAndReplace);
            dataHelper = CreateDataHelper();
            searchAndReplace = dataHelper.GetById(id);
            page = DataFactory.Instance.GetPage(new PageReference(searchAndReplace.Pages[0].PageId, searchAndReplace.Pages[0].WorkId));
            propertyValue = page.Property["MainContent"].Value.ToString();
            Assert.AreEqual(MAIN_CONTENT.Replace(searchAndReplace.Find, searchAndReplace.Replace), propertyValue);

            Cleanup();
            CreateTestData();

            searchAndReplace = new SearchAndReplace
            {
                Created = DateTime.Now,
                CreatedBy = "Tester",
                StartPage = _sarRootId.ToString(),
                StartPageOnly = true,
                Find = "<br />",
                Replace = "<a href=\"#link\" title=\"a link\">a link</a>"
            };

            method.Invoke(null, new object[] { searchAndReplace });


            // assert values in page object
            id = searchAndReplace.Id;
            _searchAndReplaces.Add(searchAndReplace);
            dataHelper = CreateDataHelper();
            searchAndReplace = dataHelper.GetById(id);
            page = DataFactory.Instance.GetPage(new PageReference(searchAndReplace.Pages[0].PageId, searchAndReplace.Pages[0].WorkId));
            propertyValue = page.Property["MainContent"].Value.ToString();
            Assert.AreEqual(MAIN_CONTENT.Replace(searchAndReplace.Find, searchAndReplace.Replace), propertyValue);        }

        [TestMethod]
        public void EPiServer_TestCanRollback()
        {
            EPiServer_TestUpdatingMasterLanguageBranch();

            IDataHelper dataHelper = CreateDataHelper();
            SearchAndReplace searchAndReplace = dataHelper.GetById(_searchAndReplaces[0].Id);

            int count = 0;
            MethodInfo canRollBackMethod = GetCanRollbackMethod();

            foreach (Page page in searchAndReplace.Pages)
            {
                bool canRollback = (bool)canRollBackMethod.Invoke(null, new object[] { page });

                if (!canRollback)
                    continue;

                count++;
            }

            Assert.AreEqual(2, count);
        }

        [TestMethod]
        public void EPiServer_TestCanPublish()
        {
            EPiServer_TestUpdatingMasterLanguageBranch();

            IDataHelper dataHelper = CreateDataHelper();
            SearchAndReplace searchAndReplace = dataHelper.GetById(_searchAndReplaces[0].Id);

            int count = 0;
            MethodInfo canPublishMethod = GetCanPublishMethod();

            foreach (Page page in searchAndReplace.Pages)
            {
                bool canRollback = (bool)canPublishMethod.Invoke(null, new object[] { page });

                if (!canRollback)
                    continue;

                count++;
            }

            Assert.AreEqual(2, count);
        }

        [TestMethod]
        public void EPiServer_TestRollback()
        {
            // pages cannot be rolled back if there was no previous version
            EPiServer_TestUpdatingMasterLanguageBranch();

            MethodInfo rollbackMethod = GetRollbackThreadedMethod();
            IDataHelper dataHelper = CreateDataHelper();
            SearchAndReplace searchAndReplace = dataHelper.GetById(_searchAndReplaces[0].Id);
            rollbackMethod.Invoke(null, new object[] { searchAndReplace.Pages });

            foreach (Page page in searchAndReplace.Pages.Where(page => page.PreviousWorkId != page.WorkId))
            {
                bool exists = true;

                try
                {
                    DataFactory.Instance.GetPage(new PageReference(page.PageId, page.WorkId));
                }
                catch (PageNotFoundException)
                {
                    exists = false;
                }

                Assert.IsFalse(exists);
            }
        }

        [TestMethod]
        public void EPiServer_TestPublish()
        {
            // pages cannot be published if they have not previously been in a published state
            EPiServer_TestUpdatingMasterLanguageBranch();

            MethodInfo publishMethod = GetPublishThreadedMethod();
            IDataHelper dataHelper = CreateDataHelper();
            SearchAndReplace searchAndReplace = dataHelper.GetById(_searchAndReplaces[0].Id);
            publishMethod.Invoke(null, new object[] { searchAndReplace.Pages });

            int publishedPages = 0;

            foreach (Page page in searchAndReplace.Pages)
            {
                PageData pageData = DataFactory.Instance.GetPage(new PageReference(page.PageId, page.WorkId));

                if (pageData.Status == VersionStatus.Published)
                    publishedPages++;
            }

            Assert.AreEqual(2, publishedPages);
        }

        [TestMethod]
        public void EPiServer_TestStringLengthLimit()
        {
            // need to confirm that one error record was created!
            MethodInfo method = GetFindAndReplaceThreadedMethod();
            Languages languages = GetLanguages();

            SearchAndReplace searchAndReplace = new SearchAndReplace
            {
                Created = DateTime.Now,
                CreatedBy = "Tester",
                Find = "toreplace",
                Replace = "TOREPLACEWASREPLACEDWITHTHIS",
                LanguageBranch = languages.MasterLanguageBranch,
                MatchCase = false,
                SearchWithinWords = false,
                StartPage = PageReference.StartPage.ID.ToString(),
                StartPageOnly = false,
                UpdateDynamicProperties = false
            };

            method.Invoke(null, new object[] { searchAndReplace });

            Guid id = searchAndReplace.Id;
            _searchAndReplaces.Add(searchAndReplace);
            IDataHelper dataHelper = CreateDataHelper();
            searchAndReplace = dataHelper.GetById(id);

            Assert.AreEqual(0, searchAndReplace.Pages.Count);
            Assert.AreEqual(1, searchAndReplace.Errors.Count);
        }

        #endregion Test methods

        private void TestFindAndReplaceInLanguageBranch(string languageBranch, ILanguageSelector languageSelector, 
            int pageCount, bool updateDynamicProperties, int dynamicPropertiesCount)
        {
            MethodInfo method = GetFindAndReplaceThreadedMethod();

            SearchAndReplace searchAndReplace = new SearchAndReplace
            {
                Created = DateTime.Now,
                CreatedBy = "Tester",
                Find = "text to replace",
                Replace = "REPLACED",
                LanguageBranch = languageBranch,
                MatchCase = false,
                SearchWithinWords = false,
                StartPage = PageReference.StartPage.ID.ToString(),
                StartPageOnly = false,
                UpdateDynamicProperties = updateDynamicProperties
            };

            method.Invoke(null, new object[] { searchAndReplace });

            Guid id = searchAndReplace.Id;
            _searchAndReplaces.Add(searchAndReplace);
            IDataHelper dataHelper = CreateDataHelper();
            searchAndReplace = dataHelper.GetById(id);

            Assert.AreEqual(pageCount, searchAndReplace.Pages.Count);

            foreach (PageData pageData in searchAndReplace.Pages.Select(page => DataFactory.Instance.GetPage(new PageReference(page.PageId, page.WorkId), languageSelector)))
            {
                if (!string.IsNullOrEmpty(languageBranch))
                    Assert.AreEqual(languageBranch, pageData.LanguageBranch);
                Assert.AreEqual(MAIN_CONTENT.Replace("text to replace", "REPLACED"), pageData["MainContent"]);
            }

            if (!updateDynamicProperties)
                return;

            Assert.AreEqual(dynamicPropertiesCount, searchAndReplace.DynamicProperties.Count);

            foreach (PageData pageData in searchAndReplace.DynamicProperties.Select(dynamicProperty => DataFactory.Instance.GetPage(new PageReference(dynamicProperty.PageId, dynamicProperty.WorkId), languageSelector)))
            {
                DynamicPropertyCollection dynamicProperties = DynamicProperty.ListForPage(pageData.PageLink);
                DynamicProperty dynamicProperty = null;

                foreach (DynamicProperty prop in dynamicProperties.Cast<DynamicProperty>().Where(prop => prop.PropertyValue.Name == "DynProp"))
                    dynamicProperty = prop;

                if (dynamicProperty == null || dynamicProperty.PropertyValue.Value == null)
                    continue;

                if (!string.IsNullOrEmpty(languageBranch))
                    Assert.AreEqual(languageBranch, dynamicProperty.LanguageBranch);

                Assert.AreEqual(MAIN_CONTENT.Replace("text to replace", "REPLACED"), dynamicProperty.PropertyValue.Value.ToString());
            }
        }

        private static IDataHelper CreateDataHelper()
        {
            Type type = Type.GetType("FortuneCookie.EPiServer.SearchAndReplace.Data.SqlDataHelper, FortuneCookie.EPiServer.SearchAndReplace");
            return (IDataHelper)Activator.CreateInstance(type);
        }

        private static Languages GetLanguages()
        {
            LanguageBranchCollection languageBranches = LanguageBranch.ListEnabled();
            Languages languages = new Languages();
            languages.MasterLanguageBranch = DataFactory.Instance.GetPage(PageReference.StartPage).MasterLanguageBranch;
            languages.SecondaryLanguageBranch = languageBranches.Where(current => current.LanguageID != DataFactory.Instance.GetPage(PageReference.StartPage).MasterLanguageBranch).First().LanguageID;
            languages.MasterLanguageSelector = new LanguageSelector(languages.MasterLanguageBranch);
            languages.SecondaryLanguageSelector = new LanguageSelector(languages.SecondaryLanguageBranch);
            return languages;
        }

        private static Type GetSearchAndReplaceHelperType()
        {
            Assembly assembly = Assembly.GetAssembly(typeof(SearchAndReplace));
            return assembly.GetType("FortuneCookie.EPiServer.SearchAndReplace.SearchAndReplaceHelper");
        }

        private static MethodInfo GetFindAndReplaceMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("FindAndReplace", BindingFlags.Static | BindingFlags.NonPublic, null, new[]{ typeof(string), typeof(SearchAndReplace), typeof(bool).MakeByRefType() }, null);
        }

        private static MethodInfo GetFindAndReplaceInHtmlMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("FindAndReplaceInHtmlString", BindingFlags.Static | BindingFlags.NonPublic);
        }

        private static MethodInfo GetFindAndReplaceThreadedMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("FindAndReplaceThreaded", BindingFlags.Static | BindingFlags.NonPublic);
        }

        private static MethodInfo GetPublishThreadedMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("PublishThreaded", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(object) }, null);
        }

        private static MethodInfo GetRollbackThreadedMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("RollbackThreaded", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(object) }, null);
        }

        private static MethodInfo GetCanRollbackMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("CanRollback", BindingFlags.Static | BindingFlags.Public);
        }

        private static MethodInfo GetCanPublishMethod()
        {
            return GetSearchAndReplaceHelperType().GetMethod("CanRollback", BindingFlags.Static | BindingFlags.Public);
        }

        private class Languages
        {
            public string MasterLanguageBranch;
            public ILanguageSelector MasterLanguageSelector;
            public string SecondaryLanguageBranch;
            public ILanguageSelector SecondaryLanguageSelector;
        }

    }

}
